<template>
    <div class="magnifier">
        <el-row style="margin-bottom: 10px;">
            <el-button @click="setOrigin">还原</el-button>
            <el-button @click="setGrey">黑白滤镜</el-button>
            <el-button @click="setLight">冷白滤镜</el-button>
            <el-button @click="setOld">怀旧滤镜</el-button>
            <el-button @click="setReverse">曝光滤镜</el-button>
            <el-button @click="setMosaic">马赛克滤镜</el-button>
        </el-row>
        <canvas ref="canvasDom" :class="{ grab: isGrab, canvas: true }"></canvas>
        <canvas ref="offCanvasDom" class="off-canvas"></canvas>
    </div>
</template>

<script setup lang="ts">
import { onMounted } from 'vue';
import { ref } from 'vue';


const canvasDom = ref<HTMLCanvasElement>(), offCanvasDom = ref<HTMLCanvasElement>();
let ctx: CanvasRenderingContext2D | null | undefined, offCtx: CanvasRenderingContext2D | null | undefined;
const isGrab = false;
let point = {}, scale = 1;
let image: HTMLImageElement;
const onMousedown = function (e: MouseEvent) {
    if (canvasDom.value) {
        point = { x: e.offsetX, y: e.offsetY };
        drawCanvasWithMagnifier(true, point)
        canvasDom.value.addEventListener('mouseup', onMouseup, { once: true })
        canvasDom.value.addEventListener('mouseleave', onMouseup, { once: true })
        canvasDom.value.addEventListener('mousemove', onMousemove)
    }
}

const onMouseup = function () {
    drawCanvasWithMagnifier(false)
    canvasDom.value?.removeEventListener('mousemove', onMousemove)
}

const onMousemove = function (e: MouseEvent) {
    point = { x: e.offsetX, y: e.offsetY };
    drawCanvasWithMagnifier(true, point)
}

const setOrigin = () => {
    const iamgeData = offCtx?.getImageData(0, 0, canvasDom.value?.width || 0, canvasDom.value?.height || 0)
    iamgeData && ctx?.putImageData(iamgeData, 0, 0, 0, 0, canvasDom.value?.width || 0, canvasDom.value?.height || 0);
}

const setGrey = () => {
    const iamgeData = offCtx?.getImageData(0, 0, canvasDom.value?.width || 0, canvasDom.value?.height || 0)
    if (iamgeData) {
        const fixedData = iamgeData.data;
        for (let i = 0; i < fixedData.length; i += 4) {
            const r = fixedData[i + 0];
            const g = fixedData[i + 1];
            const b = fixedData[i + 2];

            const grey = r * 0.3 + g * 0.59 + b * 0.11;
            fixedData[i + 0] = grey;
            fixedData[i + 1] = grey;
            fixedData[i + 2] = grey;
        }
    }
    iamgeData && ctx?.putImageData(iamgeData, 0, 0, 0, 0, canvasDom.value?.width || 0, canvasDom.value?.height || 0);
}

// 冷白，调亮就把每个值往上调，调暗就往下调
const setLight = () => {
    const iamgeData = offCtx?.getImageData(0, 0, canvasDom.value?.width || 0, canvasDom.value?.height || 0)
    if (iamgeData) {
        const fixedData = iamgeData.data;
        const luminance = 60
        for (let i = 0; i < fixedData.length; i += 4) {
            const r = fixedData[i + 0] + luminance;
            const g = fixedData[i + 1] + luminance;
            const b = fixedData[i + 2] + luminance;

            fixedData[i + 0] = r;
            fixedData[i + 1] = g;
            fixedData[i + 2] = b;
        }
    }
    iamgeData && ctx?.putImageData(iamgeData, 0, 0, 0, 0, canvasDom.value?.width || 0, canvasDom.value?.height || 0);

}

// 怀旧，老照片是有点偏黄的黑白灰照片，红 + 绿 = 黄
const setOld = () => {
    const iamgeData = offCtx?.getImageData(0, 0, canvasDom.value?.width || 0, canvasDom.value?.height || 0)
    if (iamgeData) {
        const fixedData = iamgeData.data;
        const luminance = 60
        for (let i = 0; i < fixedData.length; i += 4) {
            const r = fixedData[i + 0] + luminance;
            const g = fixedData[i + 1] + luminance;
            const b = fixedData[i + 2] + luminance;

            fixedData[i + 0] = r * 0.28 + g * 0.72 + b * 0.22;
            fixedData[i + 1] = r * 0.25 + g * 0.63 + b * 0.13;
            fixedData[i + 2] = r * 0.17 + g * 0.66 + b * 0.13;
        }
    }
    iamgeData && ctx?.putImageData(iamgeData, 0, 0, 0, 0, canvasDom.value?.width || 0, canvasDom.value?.height || 0);

}

const setReverse = () => {
    const iamgeData = offCtx?.getImageData(0, 0, canvasDom.value?.width || 0, canvasDom.value?.height || 0)
    if (iamgeData) {
        const fixedData = iamgeData.data;
        for (let i = 0; i < fixedData.length; i += 4) {
            const r = fixedData[i + 0];
            const g = fixedData[i + 1];
            const b = fixedData[i + 2];

            fixedData[i + 0] = 255 - r;
            fixedData[i + 1] = 255 - g;
            fixedData[i + 2] = 255 - b;
        }
    }
    iamgeData && ctx?.putImageData(iamgeData, 0, 0, 0, 0, canvasDom.value?.width || 0, canvasDom.value?.height || 0);

}

// 原理:先定义一个马赛克范围参数，该参数越大，马赛克的格子就越大。通过该参数去到当前正在操作的像素的四周像素，并将这些像素的颜色值求出一个平均值，然后该像素四周的像素都使用求出来的颜色值。
// todo 算法优化 如递归处理马赛克格子的颜色均值
const setMosaic = () => {
    if (!canvasDom.value) return;
    const width = 250 * 1.5, height = 410 * 1.5;
    const iamgeData = offCtx?.getImageData(0, 0, width, height)
    const iamgeDataTmp = JSON.parse(JSON.stringify(iamgeData));
    console.log('setMosaic', iamgeData)
    if (iamgeData && iamgeDataTmp) {
        const fixedData = iamgeData.data;
        const tmpData = iamgeDataTmp.data;
        console.log('begin', fixedData)
        const size = 10;
        const totalNum = size * size;
        for (let i = 0; i < height; i += size) {
            for (let j = 0; j < width; j += size) {
                let totalR = 0, totalG = 0, totalB = 0;

                for (let dx = 0; dx < size; dx++) {

                    for (let dy = 0; dy < size; dy++) {
                        const x = i + dx;
                        const y = j + dy;
                        const p = x * width + y;

                        totalR += tmpData[p * 4 + 0];
                        totalG += tmpData[p * 4 + 1];
                        totalB += tmpData[p * 4 + 2];
                    }
                }

                // const p = i * width + j;
                const r = totalR / totalNum;
                const g = totalG / totalNum;
                const b = totalB / totalNum;


                for (let dx = 0; dx < size; dx++) {
                    for (let dy = 0; dy < size; dy++) {
                        const x = i + dx;
                        const y = j + dy;
                        const p = x * width + y;
                        fixedData[p * 4 + 0] = r;
                        fixedData[p * 4 + 1] = g;
                        fixedData[p * 4 + 2] = b;
                    }
                }

            }

        }
        console.log('end')
    }
    iamgeData && ctx?.putImageData(iamgeData, 0, 0, 0, 0, 250 * 1.5, 410 * 1.5);
}


onMounted(async () => {
    if (canvasDom.value) {
        ctx = canvasDom.value.getContext('2d');
        canvasDom.value.width = canvasDom.value.offsetWidth;
        canvasDom.value.height = canvasDom.value.offsetHeight;
        if (offCanvasDom.value) {
            offCtx = offCanvasDom.value.getContext('2d');
            offCanvasDom.value.width = canvasDom.value.width * scale;
            offCanvasDom.value.height = canvasDom.value.height * scale;
        }
        image = new Image();
        image.onload = function () {
            ctx?.drawImage(image, 0, 0, 250 * 1.5, 410 * 1.5)
            offCtx?.drawImage(image, 0, 0, 250 * 1.5 * scale, 410 * 1.5 * scale)
        }
        image.src = (await import('../images/zhigengniao.jpg')).default;

        // canvasDom.value.addEventListener('mousedown', onMousedown);
    }
})

const drawCanvasWithMagnifier = (isShow: any, point?: any) => {
    ctx?.clearRect(0, 0, canvasDom.value?.width || 0, canvasDom.value?.height || 0)
    ctx?.drawImage(image, 0, 0, 250 * 1.5, 410 * 1.5)
    if (isShow) {
        drawMagnifier(point)
    }
}

const drawMagnifier = (point: { x: number; y: number; }) => {
    const mr = 100;
    const imageLG_cx = point.x * scale;
    const imageLG_cy = point.y * scale;

    const sx = imageLG_cx - mr * scale / 2;
    const sy = imageLG_cy - mr * scale / 2;

    const dx = point.x - mr * scale / 2;
    const dy = point.y - mr * scale / 2;

    if (ctx) {
        ctx.save();
        ctx.lineWidth = 10;
        ctx.strokeStyle = '#333'
        ctx.beginPath()
        ctx.arc(point.x, point.y, mr, 0, Math.PI * 2)
        ctx.stroke();
        ctx.closePath();
        ctx.clip();
        offCanvasDom.value && ctx.drawImage(offCanvasDom.value, sx, sy, scale * mr, scale * mr, dx, dy, scale * mr, scale * mr)

        ctx.restore();
    }
}

</script>

<style scoped lang="scss">
.magnifier {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    // height: 600px;

    .canvas {
        // margin: 5% auto;
        width: 80%;
        height: 80%;
        border: 1px solid #00ced1;
    }

    .off-canvas {
        display: none;
    }
}

.grab {
    cursor: grab;
}
</style>